package org.xxmi.cors.common.map;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import java.util.ArrayList;
import java.util.List;

/**
 * 地图工具类
 * 
 * @description
 * @author xxmi.org
 * @version v1.0.0
 * @date 2016年6月24日-上午8:46:35
 */
public class Geoutils {
	/**
	 * 判断点是否在矩形内
	 * 
	 * @param {Point} point 点对象
	 * @param {Bounds} bounds 矩形边界对象
	 * @returns {Boolean} 点在矩形内返回true,否则返回false
	 */
	public static boolean isPointInRect(Point point, Bounds bounds) {
		Point sw = bounds.getSouthWest(); // 西南脚点
		Point ne = bounds.getNorthEast(); // 东北脚点
		return (point.getLng() >= sw.getLng() && point.getLng() <= ne.getLng()
				&& point.getLat() >= sw.getLat() && point.getLat() <= ne
				.getLat());
	}

	/**
	 * 判断点是否多边形内
	 * 
	 * @param {Point} point 点对象
	 * @param {Polyline} polygon 多边形对象
	 * @returns {Boolean} 点在多边形内返回true,否则返回false
	 */
	public static boolean isPointInPolygon(Point point, Polygon polygon) {
		// 首先判断点是否在多边形的外包矩形内，如果在，则进一步判断，否则返回false
		Bounds polygonBounds = polygon.getBounds();
		if (!isPointInRect(point, polygonBounds)) {
			return false;
		}

		List<Point> pts = polygon.getPath();// 获取多边形点

		// 下述代码来源：http://paulbourke.net/geometry/insidepoly/，进行了部分修改
		// 基本思想是利用射线法，计算射线与多边形各边的交点，如果是偶数，则点在多边形外，否则
		// 在多边形内。还会考虑一些特殊情况，如点在多边形顶点上，点在多边形边上等特殊情况。

		int N = pts.size();
		boolean boundOrVertex = true; // 如果点位于多边形的顶点或边上，也算做点在多边形内，直接返回true
		int intersectCount = 0;// cross points count of x
		double precision = 2e-10; // 浮点类型计算时候与0比较时候的容差
		Point p1, p2; // neighbour bound vertices
		Point p = point; // 测试点

		p1 = pts.get(0);// left vertex
		for (int i = 1; i <= N; ++i) {// check all rays
			if (p.equals(p1)) {
				return boundOrVertex;// p is an vertex
			}

			p2 = pts.get(i % N);// right vertex
			if (p.getLat() < Math.min(p1.getLat(), p2.getLat())
					|| p.getLat() > Math.max(p1.getLat(), p2.getLat())) {
				p1 = p2;
				continue;// next ray left point
			}

			if (p.getLat() > Math.min(p1.getLat(), p2.getLat())
					&& p.getLat() < Math.max(p1.getLat(), p2.getLat())) {
				if (p.getLng() <= Math.max(p1.getLng(), p2.getLng())) {
					if (p1.getLat() == p2.getLat()
							&& p.getLng() >= Math.min(p1.getLng(), p2.getLng())) {
						return boundOrVertex;
					}

					if (p1.getLng() == p2.getLng()) {
						if (p1.getLng() == p.getLng()) {
							return boundOrVertex;
						} else {// before ray
							++intersectCount;
						}
					} else {// cross point on the left side
						double xinters = (p.getLat() - p1.getLat())
								* (p2.getLng() - p1.getLng())
								/ (p2.getLat() - p1.getLat()) + p1.getLng();
						if (Math.abs(p.getLng() - xinters) < precision) {
							return boundOrVertex;
						}

						if (p.getLng() < xinters) {// before ray
							++intersectCount;
						}
					}
				}
			} else {// special case when ray is crossing through the vertex
				if (p.getLat() == p2.getLat() && p.getLng() <= p2.getLng()) {
					Point p3 = pts.get((i + 1) % N); // next vertex
					if (p.getLat() >= Math.min(p1.getLat(), p3.getLat())
							&& p.getLat() <= Math.max(p1.getLat(), p3.getLat())) {
						++intersectCount;
					} else {
						intersectCount += 2;
					}
				}
			}
			p1 = p2;// next ray left point
		}

		if (intersectCount % 2 == 0) {// 偶数在多边形外
			return false;
		} else { // 奇数在多边形内
			return true;
		}
	}

	/**
	 * Bounds 边界 drawData json 格式数据解析<br>
	 * { <br>
	 * name:"baidu|google|tuba", 地图名称 <br>
	 * sw:{"lng":106.313689,"lat":29.608434}, 西南脚点 <br>
	 * ne:{"lng":106.865608,"lat":29.756036}} 东北脚点 <br>
	 * }<br>
	 * 
	 * @param drawData
	 *            页面绘画的数据
	 * @return
	 */
	public static Bounds getBounds(String drawData) {
		JSONObject jsonObject = JSONObject.parseObject(drawData);
		String name = "baidu"; // 地图名称
		// 西南脚点
		JSONObject swObject = jsonObject.getJSONObject("sw");
		Point swPoint = new Point(swObject.getDouble("lng"),
				swObject.getDouble("lat"));
		// 东北脚点
		JSONObject neObject = jsonObject.getJSONObject("ne");
		Point nePoint = new Point(neObject.getDouble("lng"),
				neObject.getDouble("lat"));
		Point sw = null;
		Point ne = null;
		switch (name) {
		case "google":
			sw = PointUtil.google_To_Baidu(swPoint.getLng(), swPoint.getLat());
			swPoint.setLng(sw.getLng());
			swPoint.setLat(sw.getLat());

			ne = PointUtil.google_To_Baidu(nePoint.getLng(), nePoint.getLat());
			nePoint.setLng(ne.getLng());
			nePoint.setLat(ne.getLat());
			break;
		case "tuba":
			sw = PointUtil.tuba_To_Baidu(swPoint.getLng(), swPoint.getLat());
			swPoint.setLng(sw.getLng());
			swPoint.setLat(sw.getLat());

			ne = PointUtil.tuba_To_Baidu(nePoint.getLng(), nePoint.getLat());
			nePoint.setLng(ne.getLng());
			nePoint.setLat(ne.getLat());
			break;
		}
		// 边界对象
		Bounds bounds = new Bounds();
		// 西南脚点
		bounds.setSwPoint(swPoint);
		// 东北脚点
		bounds.setNePoint(nePoint);
		return bounds;
	}

	/**
	 * Polygon 多边形 drawData json 格式数据解析<br>
	 * { <br>
	 * name:"baidu|google|tuba", 地图名称 <br>
	 * points":[{"lng":106.313689,"lat":29.756036}] , 边界点，矩形|多边形的点 <br>
	 * sw:{"lng":106.313689,"lat":29.608434}, 西南脚点 <br>
	 * ne:{"lng":106.865608,"lat":29.756036}} 东北脚点 <br>
	 * }<br>
	 * 
	 * @param drawData
	 *            页面绘画的数据
	 * @return Polygon 多边形
	 */
	public static Polygon getPolygon(String drawData) {
		// 边界对象
		Bounds bounds = getBounds(drawData);

		JSONObject jsonObject = JSONObject.parseObject(drawData);
//		String name = jsonObject.getString("name");
		String name = "baidu"; // 地图名称

		// 边界点
		JSONArray pointsArray = jsonObject.getJSONArray("points");
		// 多边形对象
		Polygon polygon = new Polygon();
		polygon.setBounds(bounds);
		List<Point> paths = new ArrayList<>();

		for (int i = 0; i < pointsArray.size(); i++) {
			Point point = null;
			Point bean = JSONObject.parseObject(pointsArray.getString(i),Point.class);
			switch (name) {
			case "google":
				point = PointUtil.google_To_Baidu(bean.getLng(), bean.getLat());
				bean.setLng(point.getLng());
				bean.setLat(point.getLat());
				break;
			case "tuba":
				point = PointUtil.tuba_To_Baidu(bean.getLng(), bean.getLat());
				bean.setLng(point.getLng());
				bean.setLat(point.getLat());
				break;
			}
			paths.add(bean);
		}
		polygon.setPath(paths);
		return polygon;
	}

}
